# -*- coding: utf - 8 -*-
"""
数据一致性验证器

负责验证迁移前后的数据一致性，提供错误处理和回滚机制。
"""

import subprocess
import logging
# import re  # noqa: F401
from typing import Dict, List, Any, Optional
try:
    from dataclasses import dataclass
except ImportError:
    # Python < 3.7 fallback
    def dataclass(cls):
        return cls

try:
    from .parser import ParsedStatement, StatementType
except ImportError:
    from parser import ParsedStatement, StatementType


@dataclass
class ValidationResult:
    """验证结果"""
    is_valid: bool
    message: str
    details: Optional[Dict[str, Any]] = None


@dataclass
class TableInfo:
    """表信息"""
    name: str
    row_count: int
    column_count: int
    columns: List[str]


class MigrationValidator:
    """迁移验证器"""

    def __init__(self, config: Optional[Dict[str, Any]] = None):
        self.logger = logging.getLogger(__name__)
        self.config = config or {}

    def validate_migration(self,
                           original_statements: List[ParsedStatement],
                           converted_statements: List[ParsedStatement],
                           kwbase_path: str = 'kwbase',
                           host: str = 'localhost',
                           port: int = 26257,
                           database: str = '',
                           insecure: bool = True) -> List[ValidationResult]:
        """
        验证迁移结果

        Args:
            original_statements: 原始语句列表
            converted_statements: 转换后语句列表
            kwbase_path: kwbase CLI工具路径
            host: KWDB服务器地址
            port: KWDB端口
            database: 数据库名
            insecure: 是否使用非安全连接

        Returns:
            验证结果列表
        """
        results = []

        # 1. 验证语句转换完整性
        results.append(self._validate_statement_completeness(original_statements, converted_statements))

        # 2. 验证表结构
        results.extend(self._validate_table_structures(converted_statements))

        # 3. 验证数据类型转换
        results.extend(self._validate_data_type_conversions(converted_statements))

        # 4. 验证索引定义
        results.extend(self._validate_index_definitions(converted_statements))

        # 5. 如果可以连接到KWDB，进行实际验证
        if self._can_connect_to_kwdb(kwbase_path, host, port, insecure):
            results.extend(self._validate_against_kwdb(
                converted_statements, kwbase_path, host, port, database, insecure
            ))

        return results

    def validate_table_counts(self,
                              source_tables: Dict[str, int],
                              target_tables: Dict[str, int]) -> ValidationResult:
        """
        验证表行数一致性

        Args:
            source_tables: 源表行数统计 {表名: 行数}
            target_tables: 目标表行数统计 {表名: 行数}

        Returns:
            验证结果
        """
        mismatches = []

        for table_name, source_count in source_tables.items():
            target_count = target_tables.get(table_name, 0)

            if source_count != target_count:
                mismatches.append({
                    'table': table_name,
                    'source_count': source_count,
                    'target_count': target_count,
                    'difference': abs(source_count - target_count)
                })

        # 检查目标表中是否有源表中没有的表
        extra_tables = set(target_tables.keys()) - set(source_tables.keys())

        if not mismatches and not extra_tables:
            return ValidationResult(
                is_valid=True,
                message="表行数验证通过",
                details={'total_tables': len(source_tables)}
            )
        else:
            return ValidationResult(
                is_valid=False,
                message=f"发现{len(mismatches)}个表行数不匹配，{len(extra_tables)}个额外表",
                details={
                    'mismatches': mismatches,
                    'extra_tables': list(extra_tables)
                }
            )

    def _validate_statement_completeness(self,
                                         original: List[ParsedStatement],
                                         converted: List[ParsedStatement]) -> ValidationResult:
        """验证语句转换完整性"""
        original_count = len(original)
        converted_count = len(converted)

        if converted_count == original_count:
            return ValidationResult(
                is_valid=True,
                message="语句转换完整性验证通过",
                details={'total_statements': original_count}
            )
        else:
            missing_count = original_count - converted_count
            return ValidationResult(
                is_valid=False,
                message=f"有{missing_count}个语句未能转换",
                details={
                    'original_count': original_count,
                    'converted_count': converted_count,
                    'missing_count': missing_count
                }
            )

    def _validate_table_structures(self, statements: List[ParsedStatement]) -> List[ValidationResult]:
        """验证表结构定义"""
        results = []

        create_table_statements = [s for s in statements if s.statement_type == StatementType.CREATE_TABLE]

        for stmt in create_table_statements:
            if not stmt.table_name:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"CREATE TABLE语句缺少表名: {stmt.original_sql[:50]}..."
                ))
                continue

            # 检查是否有列定义
            if not stmt.columns or len(stmt.columns) == 0:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"表{stmt.table_name}没有列定义"
                ))
                continue

            # 检查主键定义
            has_primary_key = (
                'PRIMARY KEY' in stmt.original_sql.upper()
                or any(col.get('auto_increment', False) for col in stmt.columns)
            )

            if not has_primary_key:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"表{stmt.table_name}缺少主键定义",
                    details={'table': stmt.table_name}
                ))

        if not results:
            results.append(ValidationResult(
                is_valid=True,
                message=f"表结构验证通过，共{len(create_table_statements)}个表"
            ))

        return results

    def _validate_data_type_conversions(self, statements: List[ParsedStatement]) -> List[ValidationResult]:
        """验证数据类型转换"""
        results = []

        # 检查是否有不支持的数据类型
        unsupported_types = []

        for stmt in statements:
            if stmt.statement_type == StatementType.CREATE_TABLE and stmt.columns:
                for col in stmt.columns:
                    col_type = col.get('type', '').upper()

                    # 检查一些可能有问题的类型转换
                    if 'ENUM' in col_type:
                        unsupported_types.append({
                            'table': stmt.table_name,
                            'column': col.get('name'),
                            'type': col_type,
                            'issue': 'ENUM类型可能需要手动处理'
                        })
                    elif 'SET' in col_type:
                        unsupported_types.append({
                            'table': stmt.table_name,
                            'column': col.get('name'),
                            'type': col_type,
                            'issue': 'SET类型可能需要手动处理'
                        })

        if unsupported_types:
            results.append(ValidationResult(
                is_valid=False,
                message=f"发现{len(unsupported_types)}个可能有问题的数据类型转换",
                details={'unsupported_types': unsupported_types}
            ))
        else:
            results.append(ValidationResult(
                is_valid=True,
                message="数据类型转换验证通过"
            ))

        return results

    def _validate_index_definitions(self, statements: List[ParsedStatement]) -> List[ValidationResult]:
        """验证索引定义"""
        results = []

        index_statements = [s for s in statements if s.statement_type == StatementType.CREATE_INDEX]

        for stmt in index_statements:
            if not stmt.table_name:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"CREATE INDEX语句缺少表名: {stmt.original_sql[:50]}..."
                ))

        if not results:
            results.append(ValidationResult(
                is_valid=True,
                message=f"索引定义验证通过，共{len(index_statements)}个索引"
            ))

        return results

    def _can_connect_to_kwdb(self, kwbase_path: str, host: str, port: int, insecure: bool) -> bool:
        """检查是否可以连接到KWDB"""
        try:
            cmd = [kwbase_path, 'sql']

            if insecure:
                cmd.append('--insecure')

            cmd.extend([
                f'--host={host}:{port}',
                '--execute', 'SELECT 1;'
            ])

            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            return result.returncode == 0

        except Exception as e:
            self.logger.debug(f"无法连接到KWDB: {e}")
            return False

    def _validate_against_kwdb(self,
                               statements: List[ParsedStatement],
                               kwbase_path: str,
                               host: str,
                               port: int,
                               database: str,
                               insecure: bool) -> List[ValidationResult]:
        """针对KWDB实例进行验证"""
        results = []

        try:
            # 获取KWDB中的表信息
            kwdb_tables = self._get_kwdb_tables(kwbase_path, host, port, database, insecure)

            # 验证表是否存在
            expected_tables = set()
            for stmt in statements:
                if stmt.statement_type == StatementType.CREATE_TABLE and stmt.table_name:
                    expected_tables.add(stmt.table_name.lower())

            existing_tables = set(table.lower() for table in kwdb_tables.keys())

            missing_tables = expected_tables - existing_tables
            extra_tables = existing_tables - expected_tables

            if missing_tables:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"KWDB中缺少{len(missing_tables)}个表",
                    details={'missing_tables': list(missing_tables)}
                ))

            if extra_tables:
                results.append(ValidationResult(
                    is_valid=False,
                    message=f"KWDB中有{len(extra_tables)}个额外的表",
                    details={'extra_tables': list(extra_tables)}
                ))

            if not missing_tables and not extra_tables:
                results.append(ValidationResult(
                    is_valid=True,
                    message=f"KWDB表验证通过，共{len(expected_tables)}个表"
                ))

        except Exception as e:
            results.append(ValidationResult(
                is_valid=False,
                message=f"KWDB验证失败: {e}"
            ))

        return results

    def _get_kwdb_tables(self,
                         kwbase_path: str,
                         host: str,
                         port: int,
                         database: str,
                         insecure: bool) -> Dict[str, TableInfo]:
        """获取KWDB中的表信息"""
        tables = {}

        try:
            # 获取表列表
            cmd = [kwbase_path, 'sql']

            if insecure:
                cmd.append('--insecure')

            cmd.extend([
                f'--host={host}:{port}',
                '--execute', 'SHOW TABLES;'
            ])

            if database:
                cmd.extend(['--database', database])

            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                # 解析表名
                lines = result.stdout.strip().split('\n')
                for line in lines:
                    line = line.strip()
                    if line and not line.startswith(('table_name', '---', '(')):
                        table_name = line.split()[0] if line.split() else line
                        if table_name:
                            tables[table_name] = TableInfo(
                                name=table_name,
                                row_count=0,
                                column_count=0,
                                columns=[]
                            )

        except Exception as e:
            self.logger.error(f"获取KWDB表信息失败: {e}")

        return tables

    def generate_validation_report(self,
                                   validation_results: List[ValidationResult],
                                   output_path: str) -> bool:
        """
        生成验证报告

        Args:
            validation_results: 验证结果列表
            output_path: 报告输出路径

        Returns:
            是否成功生成
        """
        try:
            report_lines = []

            # 报告头
            report_lines.extend([
                "# 迁移验证报告",
                f"生成时间: {self._get_current_timestamp()}",
                "",
                "## 验证摘要",
                ""
            ])

            # 统计验证结果
            total_checks = len(validation_results)
            passed_checks = len([r for r in validation_results if r.is_valid])
            failed_checks = total_checks - passed_checks

            report_lines.extend([
                f"- 总检查项: {total_checks}",
                f"- 通过检查: {passed_checks}",
                f"- 失败检查: {failed_checks}",
                f"- 通过率: {passed_checks / total_checks * 100:.1f}%",
                ""
            ])

            # 详细结果
            report_lines.append("## 详细验证结果")
            report_lines.append("")

            for i, result in enumerate(validation_results, 1):
                status = "✅ 通过" if result.is_valid else "❌ 失败"
                report_lines.append(f"### {i}. {status}")
                report_lines.append(f"**消息**: {result.message}")

                if result.details:
                    report_lines.append("**详细信息**:")
                    for key, value in result.details.items():
                        if isinstance(value, list):
                            report_lines.append(f"- {key}: {len(value)} 项")
                            for item in value[:5]:  # 只显示前5项
                                report_lines.append(f"  - {item}")
                            if len(value) > 5:
                                report_lines.append(f"  - ... 还有 {len(value) - 5} 项")
                        else:
                            report_lines.append(f"- {key}: {value}")

                report_lines.append("")

            # 建议
            if failed_checks > 0:
                report_lines.extend([
                    "## 建议",
                    "",
                    "1. 检查失败的验证项并进行相应修正",
                    "2. 重新运行迁移工具进行转换",
                    "3. 在生产环境执行前进行充分测试",
                    ""
                ])

            # 写入报告文件
            with open(output_path, 'w', encoding='utf - 8') as f:
                f.write('\n'.join(report_lines))

            self.logger.info(f"成功生成验证报告: {output_path}")
            return True

        except Exception as e:
            self.logger.error(f"生成验证报告失败: {e}")
            return False

    def _get_current_timestamp(self) -> str:
        """获取当前时间戳"""
        from datetime import datetime
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
